live555 源码阅读 liveMedia : 主要的媒体处理逻辑。
UsageEnvironment : 处理事件循环和任务调度。
groupsock : 网络层,处理组播和单播的socket通信。
BasicUsageEnvironment : 提供基本的环境支持,是UsageEnvironment的一个简单实现。
https://blog.csdn.net/C1033177205/article/details/104331224
事件循环主要处理三类事件:Socket 事件、触发器事件和定时任务。
SDP 1 2 3 4 5 6 7 8 SDP(Session Description Protocol,会议描述协议)是一个用于描述多媒体通信会话的协议。SDP主要用于协商和定义参与会话的各方在通信时需要的媒体格式和网络参数。它常用于多媒体通信和流媒体应用中,如视频会议、VoIP(网络电话)、实时流媒体播放等。 SDP的主要功能包括: 会话描述:定义会话的基本属性,如会话的名称、时间、地点等。 媒体信息:描述会话中传输的多媒体流的信息,包括媒体类型(音频、视频等)、编码格式、传输地址和端口等。 会话参数协商:在会话的参与方之间协商媒体参数,以确保所有参与方能够兼容和正确处理媒体流。 SDP通常与其他协议一起使用,如SIP(会话发起协议)和RTSP(实时流协议),以建立和管理多媒体会话。SDP本身不传输媒体数据,而是用于描述和协商会话参数,媒体数据的实际传输通常通过RTP(实时传输协议)等协议进行。
TaskScheduler和BasicTaskScheduler 任务调度的模块,TaskScheduler定义了接口,BasicTaskScheduler继承BasicTaskScheduler0,BasicTaskScheduler0继承TaskScheduler。
testRTSPClient
创建scheduler 及env;
打开播放地址;
让程序进入消息循环跑起来
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 int main (int argc, char ** argv) { TaskScheduler* scheduler = BasicTaskScheduler::createNew (); UsageEnvironment* env = BasicUsageEnvironment::createNew (*scheduler); if (argc < 2 ) { usage (*env, argv[0 ]); return 1 ; } openURL (*env, argv[0 ], argv[1 ]); if (argc >= 3 && strstr (argv[2 ],"tcp" )){ REQUEST_STREAMING_OVER_TCP = true ; } env->taskScheduler ().doEventLoop (&eventLoopWatchVariable); return 0 ;
DESCRIBE 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 int RTSPClient::openConnection () { do { if (!parseRTSPURL (envir (), fBaseURL, username, password, destAddress, urlPortNum, &urlSuffix)) break ; portNumBits destPortNum = fTunnelOverHTTPPortNum == 0 ? urlPortNum : fTunnelOverHTTPPortNum; if (username != NULL || password != NULL ) { fCurrentAuthenticator.setUsernameAndPassword (username, password); delete [] username; delete [] password; } fInputSocketNum = fOutputSocketNum = setupStreamSocket (envir (), Port (0 ), destAddress.getFamily ()); if (fInputSocketNum < 0 ) break ; ignoreSigPipeOnSocket (fInputSocketNum); fServerAddress = destAddress; int connectResult = connectToServer (fInputSocketNum, destPortNum); if (connectResult < 0 ) break ; else if (connectResult > 0 ) { envir ().taskScheduler ().setBackgroundHandling (fInputSocketNum, SOCKET_READABLE|SOCKET_EXCEPTION, (TaskScheduler::BackgroundHandlerProc*)&incomingDataHandler, this );
发出DESCRIBE
建立TCP连接int connectResult = openConnection();
RTSP命令封装发出。 socket回调函数incomingDataHandler读取RTSP命令响应。
handleResponseBytes处理RTSP命令的响应,解析各种RTSP响应头字段,如”RTP-Info:””Range:”等等,对于SETUP/PLAY等命令,会有一些不同的处理。然后根据responseCode,看是否正常,常见的错误像403,401。302则需要重定向,200则正常。
回到DESCRIBE命令 回调函数continueAfterDESCRIBE
,根据SDP信息创建MediaSession,并setupNextSubsession
1 2 3 4 5 6 7 8 9 10 11 12 13 14 scs.session = MediaSession::createNew (env, sdpDescription); delete [] sdpDescription; if (scs.session == NULL ) { break ; } else if (!scs.session->hasSubsessions ()) { env << *rtspClient << "This session has no media subsessions (i.e., no \"m=\" lines)\n" ; break ; } scs.iter = new MediaSubsessionIterator (*scs.session); setupNextSubsession (rtspClient); return ; } while (0 );
MediaSession createNew函数 主要是initializeWithSDP
函数,其中会解析出封装协议及编码方式,后面需要根据此来创建Source。每一个”m=”字段会创建一个MediaSubsession,最后所有MediaSubsession会存放到链表MediaSubsessionIterator里。
setup MediaSubsession
setupNextSubsession(RTSPClient* rtspClient) { 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 UsageEnvironment& env = rtspClient->envir(); // alias StreamClientState& scs = ((ourRTSPClient*)rtspClient)->scs; // alias scs.subsession = scs.iter->next(); if (scs.subsession != NULL) { if (!scs.subsession->initiate()) { //init失败,setup下个链接 setupNextSubsession(rtspClient); // give up on this subsession; go to the next one } else { //init成功,发送SETUP命令, rtspClient->sendSetupCommand(*scs.subsession, continueAfterSETUP, False, REQUEST_STREAMING_OVER_TCP); } return; } //全部链接都建立好了,发送PLAY scs.duration = scs.session->playEndTime() - scs.session->playStartTime(); rtspClient->sendPlayCommand(*scs.session, continueAfterPLAY); }
PLAY 发送PLAY命令后,等待服务器响应并解析。还是在handleResponseBytes中对PLAY响应有专门的处理handlePLAYResponse,主要是解析各种响应参数 Scale、Speed、Range、RTP-Info,RTP-Info中可以携带RTP包的信息如URL,序号,时间戳,如:
1 2 3 4 5 6 7 unsigned RTSPClient::sendPlayCommand (MediaSession& session, responseHandler* responseHandler, double start, double end, float scale, Authenticator* authenticator) { if (fCurrentAuthenticator < authenticator) fCurrentAuthenticator = *authenticator; sendDummyUDPPackets (session); return sendRequest (new RequestRecord (++fCSeq, "PLAY" , responseHandler, &session, NULL , 0 , start, end, scale)); }
基本流程 初始化和连接 RTSP 客户端首先需要初始化和建立连接。这个过程包括解析 SDP(Session Description Protocol)信息,建立 RTP(Real-Time Transport Protocol)套接字等。
1 2 3 4 5 6 TaskScheduler* scheduler = BasicTaskScheduler::createNew (); env = BasicUsageEnvironment::createNew (*scheduler); RTSPServer* rtspServer = RTSPServer::createNew (*env, port); if (rtspServer == nullptr ) { *env << "Failed to create RTSP server: " << env->getResultMsg () << "\n" ; }
1 2 UsageEnvironment& env = rtspClient->envir (); StreamClientState& scs = ((ourRTSPClient*)rtspClient)->scs;
env
:获取 RTSP 客户端的环境对象。
scs
:获取 RTSP 客户端的状态对象。
SDP 解析 接收到 SDP 信息后,客户端会根据 SDP 信息中的内容来初始化相关的流媒体会话。例如,如果 SDP 中包含 IP 地址,客户端会使用这个地址来建立 RTP 套接字。
GroupSock GroupSock是Live555对网络接口的封装,支持UDP/TCP,同时也支持单播/组播。
1 udpGroupsock = new Groupsock(*env, udpIP, udpPort, ttl);
通过IP及端口,就可以创建一个GroupSock,liveMedia中的模块就可以通过该socket进行网络数据的读写。
1 2 3 4 5 6 7 8 9 if (strcmp (fCodecName, "MP2T" ) == 0 ) { fRTPSource = SimpleRTPSource::createNew (env (), fRTPSocket, fRTPPayloadFormat, fRTPTimestampFrequency, "video/MP2T" , 0 , False); fReadSource = MPEG2TransportStreamFramer::createNew (env (), fRTPSource); } SimpleRTPSource:根据编码格式创建 RTP 源。 MPEG2TransportStreamFramer:创建帧处理器,处理 RTP 包中的 MPEG-2 传输流。
发送SETUP 1 rtspClient->sendSetupCommand(*scs.subsession, continueAfterSETUP, False, REQUEST_STREAMING_OVER_TCP);
处理服务器响应 1 2 3 4 5 6 7 8 9 10 11 if (streamUsingTCP) { // 使用 TCP 传输 transportTypeStr = "/TCP;unicast"; portTypeStr = ";interleaved"; rtpNumber = fTCPStreamIdCount++; rtcpNumber = fTCPStreamIdCount++; } else { // 使用 UDP 传输 transportTypeStr = "/UDP;unicast"; portTypeStr = ";client_port"; rtpNumber = subsession.clientPort().port(); rtcpNumber = subsession.rtcpIsMuxed() ? rtpNumber : rtpNumber + 1; }
parseTransportParams
:解析服务器响应中的传输参数,设置子会话的传输地址和端口。
处理播放和事件循环 播放过程中,客户端会通过事件循环处理 RTP 数据包
1 2 3 4 5 6 void BasicTaskScheduler0::doEventLoop (char volatile * watchVariable) { while (1 ) { if (watchVariable != NULL && *watchVariable != 0 ) break ; SingleStep (); } }
doEventLoop
:事件循环,通过不断调用 SingleStep
方法来处理不同类型的事件
RTSP 服务器初始化 在Live555中,RTSP服务器由RTSPServer
类实现
1 2 3 4 5 6 TaskScheduler* scheduler = BasicTaskScheduler::createNew (); env = BasicUsageEnvironment::createNew (*scheduler); RTSPServer* rtspServer = RTSPServer::createNew (*env, port); if (rtspServer == nullptr ) { *env << "Failed to create RTSP server: " << env->getResultMsg () << "\n" ; }
媒体会话 1 2 ServerMediaSession* sms = ServerMediaSession::createNew (*env, sessionName, sessionDescription, streamDescription); rtspServer->addServerMediaSession (sms);
RTSP请求的处理由RTSPServer
类和RTSPServer::RTSPClientConnection
类完成。RTSPServer
监听客户端连接,当有新的RTSP请求到达时,会创建一个RTSPServer::RTSPClientConnection
对象来处理该请求
RTSP请求的处理由RTSPServer类和RTSPServer::RTSPClientConnection类完成。RTSPServer监听客户端连接,当有新的RTSP请求到达时,会创建一个RTSPServer::RTSPClientConnection对象来处理该请求
1 2 3 4 5 6 7 8 9 void RTSPServer::RTSPClientConnection::handleCmd_OPTIONS () { snprintf ((char *)fResponseBuffer, sizeof (fResponseBuffer), "RTSP/1.0 200 OK\r\n" "CSeq: %s\r\n" "Public: %s\r\n\r\n" , fCurrentCSeq, "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE, GET_PARAMETER, SET_PARAMETER" ); send (fClientOutputSocket, (char const *)fResponseBuffer, strlen ((char const *)fResponseBuffer), 0 ); }
媒体数据的实际传输通过RTP(实时传输协议)进行。每个媒体子会话都会创建一个RTPSink
对象来发送RTP包。
1 2 RTPSink* sink = RTPSink::createNew(*env, rtpGroupsock, 96); sink->startPlaying(source, NULL, NULL);
client 发出 teardown请求是 关闭会话并释放资源